import mongoose from 'mongoose'
import { resolve } from 'path'
const Schema = mongoose.Schema
const Mixed = Schema.Types.Mixed
import bcrypt from 'bcrypt'
// 加密等级1-10
const SALT_WORK_FACTOR = 10
// 最大登录次数
const MAX_LOGIN_ATTEMPTS = 5
// 超过次数之后锁定时间
const LOCK_TIME = 2 * 60 * 60 * 1000

const userSchema = new Schema({
  userName: {
    required: true,
    unique: true,
    type: String
  },
  email: {
    required: true,
    unique: true,
    type: String
  },
  password: {
    unique: true,
    type: String
  },
  loginAttempts: {
    type: Number,
    required: true,
    default: 0
  },
  lockUntil: Number,
  meta: {
    createdAt: {
      type: Date,
      default: Date.now()
    },
    updatedAt: {
      type: Date,
      default: Date.now()
    }
  }
})

// 增加一个虚拟字段 不需要存入数据库中
userSchema.virtual('isLocked').get(function () {
  // 两次的取反让结果成为一个boolean lockUntil是被锁定的时间
  return !!(this.lockUntil && this.lockUntil > Date.now())
})

// 增加中间件
userSchema.pre('save', function (next) {
  if (this.isNew) {
    this.meta.createdAt = this.meta.updatedAt = Date.now()
  } else {
    this.meta.updatedAt = Date.now()
  }
  next()
})

// 判断密码等是否改动 如果改动的话 进行加密处理
userSchema.pre('save', function (next) {
  if (!this.isModified('password')) return next()
  // 加密
  bcrypt.genSalt(SALT_WORK_FACTOR, (err, salt) => {
    if (err) return next(err)

    bcrypt.hash(this.password, salt, (error, hash) => {
      if (error) return next(error)

      this.password = hash
      next()
    })
  })
  next()
})

// 实例方法 比对密码 具备修改数据能力的
userSchema.methods = {
  comparePassword: (_password, password) => {
    return new Promise((resolve, reject) => {
      bcrypt.compare(_password, password, (err, isMatch) => {
        if (!err) resolve(isMatch)
        else reject(err)
      })
    })
  },
  incLoginAttepts: user => {
    return new Promise((resolve, reject) => {
      // 锁定时间到期了解除锁定
      if (this.lockUntil && this.lockUntil < Date.now()) {
        // model.update(condition, doc, [options], callback)
        // condition -- 匹配条件对象类型 doc -- 修改内容是什么对象类型 callback -- 回调函数 判断是否修改成功
        this.update(
          {
            $set: {
              loginAttempts: 1
            },
            // 删除lockUntil(锁定时间)
            $unset: {
              lockUntil: 1
            }
          },
          err => {
            if (!err) resolve(true)
            else reject(err)
          }
        )
      } else {
        let updates = {
          // 自增长 每次让登录次数loginAttempts+1
          $inc: {
            loginAttempts: 1
          }
        }
        // 如果当前是未被锁定的状态并且登录次数是超过了最大限制的次数 则需要设定lockUntil(锁定时间+2小时)
        if (this.loginAttempts + 1 >= MAX_LOGIN_ATTEMPTS && !this.isLocked) {
          updates.$set = {
            lockUntil: Date.now() + LOCK_TIME
          }
        }
        this.update(updates, err => {
          if (!err) resolve(true)
          else reject(err)
        })
      }
    })
  }
}

mongoose.model('User', userSchema)
